import java.io.DataOutputStream;
import java.nio.MappedByteBuffer;
import java.io.IOException;

import org.json.JSONArray;
import org.json.JSONObject;

/*
 * Interface Слово - это одно из 3-х:
 * а) слово в win1251 если конвертируется без потерь - class Word
 * б) слово в Unicode иначе - class WordUnicode
 * в) пробел - class Space
 */
abstract class WordInterface implements Comparable<WordInterface> {

    /*
     * Получение числа символов в слове
     */
    abstract int length();

    /*
     * Сравнение в лексиграфическом порядке.
     * Использется только для сравнение типов Word, так как только они используются
     * в ключах дерева
     */
    public abstract int compareTo(final WordInterface b);

    abstract JSONObject toJSON();

    abstract void write(DataOutputStream dos) throws IOException;

    static JSONArray toJSON(Iterable<WordInterface> words) {
        JSONArray ar = new JSONArray();
        for (WordInterface w : words) {
            ar.put(w.toJSON());
        }
        return ar;
    }

    /*
     * Фабрика, создающая объекты из JSON
     * Используется для сериализации AVLTree
     */
    static WordInterface fromJSON(final JSONObject jo) {
        if (jo.has("W"))
            return new Word((String) jo.get("W"));
        if (jo.has("U"))
            return new WordUnicode((String) jo.get("U"));
        if (jo.has("S"))
            return new Space((Integer) jo.get("S"));

        throw new IllegalArgumentException(jo.toString());
    }

    static WordInterface fromString(final char type, final String token) {
        if (type == 'W')
            return new Word(token);
        if (type == 'U')
            return new WordUnicode(token);
        if (type == 'S') {
            Integer value = Integer.valueOf(token);
            return new Space(value);
        }

        throw new IllegalArgumentException("Type-" + type + ", token-" + token);
    }

    static WordInterface fromBinary(MappedByteBuffer wbb) throws IOException {
        final byte type = wbb.get();
        if (type == 0x57) { // W
            byte[] d = TreeBinaryReaderWriter.readByteArray(wbb);
            return new Word(d);
        }
        if (type == 0x55) { // U
            final int len = wbb.getInt();
            final char[] d = new char[len];
            for (int i = 0; i < len; i++) {
                char b = wbb.getChar();
                d[i] = b;
            }
            return new WordUnicode(d);
        }
        if (type == 0x53) { // S
            byte count = wbb.get();
            return new Space(count);
        }

        throw new IllegalArgumentException("Type-" + type);
    }
}